/**************************************************************************
 * 文件名  ：iic_software.c
 * 描述    ：软件模拟IIC程序  
****************************************************************************/
#include "iic_software.h"
#include "delay.h"
#include "stdio.h"

#ifdef USE_SOFTWARE_IIC  // 使用软件IIC（注：软件IIC和硬件IIC只能同时使用一种）
/* IIC软件模拟时序延时函数 */
void iic_delay(uint16_t us)
{
    delay_us(us);
}

/* IIC起始信号 */
void set_iic_sda_mode(uint8_t mode)
{
    GPIO_InitTypeDef GPIO_InitStruct;
    if(mode > 0)
    {// 设置为输出模式
        GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_OD; // 开漏或推挽，外部需要接上拉电阻
    }
    else
    {// 设置为输入模式
        GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU; // 浮空或上拉，外部需要接上拉电阻
    }
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStruct.GPIO_Pin = IIC_SDA_PIN;
    GPIO_Init(IIC_SDA_PORT, &GPIO_InitStruct);
}

/* IIC起始信号 */
void iic_start(void)
{
    SET_IIC_SDA(1);
    SET_IIC_SCL(1);
    iic_delay(5);
    SET_IIC_SDA(0);     
    SET_IIC_SCL(0);          
    iic_delay(5);  
}

/* IIC停止信号 */
void iic_stop(void)
{
    SET_IIC_SCL(0);
    SET_IIC_SDA(0);                
    iic_delay(5);                    
    SET_IIC_SCL(1);           
    SET_IIC_SDA(1);                  
    iic_delay(5);
}

/* IIC发送应答信号 */
uint8_t iic_send_ack(int ack)
{
    set_iic_sda_mode(1);
    if(ack == 1)
    {// 发送ACk
        SET_IIC_SDA(1); 
    }
    else if(ack == 0)
    {// 发送NACK
        SET_IIC_SDA(0); 
    }
    else
    {
        return 0;  // 入参有误，发送失败
    }         
    SET_IIC_SCL(1);     
    iic_delay(5);     
    SET_IIC_SCL(0);     
    iic_delay(5);    

    return 1;  // 发送成功
}

/* IIC接收应答信号 */
uint8_t iic_wait_ack(void)
{
    uint8_t ack = 0;
    uint8_t timeout = 5;

    SET_IIC_SDA(1); 
    set_iic_sda_mode(0);  // SDA输入模式
    SET_IIC_SCL(1);           
    iic_delay(5);  
    while(timeout--)       
    {// 等待从设备发送ACK
        if(READ_IIC_SDA == 0)
        {// 读到应答信号
            ack = 1;  
        }
        else
        {// 没有读到应答信号，继续等待
            iic_delay(1);
            if(timeout == 0)
            {// 等待超时
                ack = 0; 
            }
        }
    }           
    SET_IIC_SCL(0);             
    iic_delay(5);        
    set_iic_sda_mode(1); // SDA输出模式
    
    return ack;
}

/* IIC总线发送1个字节数据 */
uint8_t iic_send_byte(uint8_t dat)
{
    uint8_t i;
    for (i = 0; i < 8; i++)
    {
        if(0x80 & dat)
        {
            SET_IIC_SDA(1); 
        }
        else
        {
            SET_IIC_SDA(0); 
        }
        dat <<= 1;
        SET_IIC_SCL(1);          
        iic_delay(5);             
        SET_IIC_SCL(0); 
        iic_delay(5);
    }

    return iic_wait_ack();  // 返回从设备应答信号
}

/* IIC总线接收1个字节数据 */
uint8_t iic_receive_byte(void)
{
    uint8_t i;
    uint8_t dat = 0;
    uint8_t bit;

    set_iic_sda_mode(0);  // SDA输入模式
    for (i = 0; i < 8; i++)
    {
        dat <<= 1;
        SET_IIC_SCL(1);
        iic_delay(5);
        if(READ_IIC_SDA == 1)
        {
            bit = 0X01;
        }
        else
        {
            bit = 0x00;  
        }
        dat |= bit;        //读数据    
        SET_IIC_SCL(0);
        iic_delay(5);
    }        
    set_iic_sda_mode(1); // SDA输出模式

    return dat;
}

/* IIC发送多个数据 */
uint8_t iic_write_bytes(uint8_t addr, uint8_t *buf, uint8_t buf_size)
{
    uint8_t i;
    uint8_t result = 0;

    iic_start();                   // 起始信号
    if(iic_send_byte(addr << 1))   // 发送设备地址(7bit地址)
    {// 收到应答，发送成功
        for (i = 0; i < buf_size; i++)  // 发送数据
        {
            if(iic_send_byte(buf[i]))
            {// 收到应答，发送成功
                result = 1;
            }
            else
            {// 没有收到应答，发送失败
                result = 0; 
            }
        }
    }
    iic_stop();                   // 发送停止信号

    return result;
}

/* IIC接收多个数据 */
uint8_t iic_receive_bytes(uint8_t addr, uint8_t *buf, uint8_t buf_size)
{
    uint8_t i;    
    uint8_t result = 0;

    iic_start();                        // 起始信号
    if(iic_send_byte((addr << 1) | 1))  // 发送设备地址(7bit地址)
    {
        for (i = 0; i < buf_size; i++)    // 连续读取数据
        {
            *buf++ = iic_receive_byte(); 
            if (i == buf_size - 1)
            {
                iic_send_ack(1);   // 最后一个数据需要回NACK
            }
            else
            {        
                iic_send_ack(0);   // 发送ACK
            }
        }
        result = 1;
    }
    iic_stop();                // 停止信号

	return result;
}

/* IIC初始化 */
void iic_init(void)
{
    GPIO_InitTypeDef GPIO_InitStruct;

    RCC_APB2PeriphClockCmd(IIC_SCL_CLOCK, ENABLE); 
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;  
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStruct.GPIO_Pin = IIC_SCL_PIN;
    GPIO_Init(IIC_SCL_PORT, &GPIO_InitStruct); 

    RCC_APB2PeriphClockCmd(IIC_SDA_CLOCK, ENABLE); 
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;  
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStruct.GPIO_Pin = IIC_SDA_PIN;
    GPIO_Init(IIC_SDA_PORT, &GPIO_InitStruct); 
}
#endif
